package com.zglc.ms.shop.filter;

import java.io.IOException;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.servlet.Filter;
import javax.servlet.FilterChain;
import javax.servlet.FilterConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.context.support.WebApplicationContextUtils;

import com.alibaba.fastjson.JSON;
import com.google.common.base.Strings;
import com.zglc.ms.base.constants.ResponseCodeHelper;
import com.zglc.ms.shop.service.CacheManagerUtil;

import io.jsonwebtoken.Claims;
import io.jsonwebtoken.impl.DefaultClaims;

@Component
public class JwtFilter implements Filter {

	private Logger logger = LoggerFactory.getLogger(getClass());

	private CacheManagerUtil cacheManagerUtil;

	@Override
	public void init(FilterConfig filterConfig) throws ServletException {
		ApplicationContext ctx = WebApplicationContextUtils.getWebApplicationContext(filterConfig.getServletContext());
		cacheManagerUtil = (CacheManagerUtil) ctx.getBean("cacheManagerUtil");
	}

	@Override
	public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain)
			throws IOException, ServletException {
		HttpServletRequest servletRequest = (HttpServletRequest) request;
		HttpServletResponse httpResponse = (HttpServletResponse) response;
		httpResponse.setHeader("Access-Control-Allow-Origin", "*");
		httpResponse.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
		httpResponse.setHeader("Access-Control-Max-Age", "3600");
		httpResponse.setHeader("Access-Control-Allow-Headers", "x-requested-with,Authorization");
		httpResponse.setHeader("Access-Control-Allow-Credentials", "true");

		String requestUrl = servletRequest.getRequestURI();

		boolean expired = false;
		String authorization = servletRequest.getHeader("Authorization");
		logger.warn("token [{}] type...requestUrl " + requestUrl, authorization);
		Claims claims = null;
		if (Strings.isNullOrEmpty(authorization)) {
			if (requestUrl.contains("/front/template/getList") || requestUrl.contains("/front/product/getDetail")) {
				request.setAttribute("claims", claims);
			} else {
				expired = true;
			}
		} else {
			String[] split = authorization.split(" ");
			if (Strings.isNullOrEmpty(split[0])) {
				if (requestUrl.contains("/front/template/getList") || requestUrl.contains("/front/product/getDetail")) {
					request.setAttribute("claims", claims);
				} else {
					logger.warn("token [{}] type null...requestUrl " + requestUrl, authorization);
					expired = true;
				}
			} else if (!split[0].equals("Bearer")) {
				if (requestUrl.contains("/front/template/getList") || requestUrl.contains("/front/product/getDetail")) {
					request.setAttribute("claims", claims);
				} else {
					logger.warn("token [{}] type error...requestUrl " + requestUrl, authorization);
					expired = true;
				}
			} else {
				String token = split[1];
				// logger.info("token[{}]requestUrl " + requestUrl, token);
				try {
					claims = JwtHelper.parseJWT(token);
					Date expiration = claims.getExpiration();
					Date date = new Date();
					if (expiration == null) {
						expired = true;
					} else if (expiration.before(date)) {
						logger.warn("token [{}] expired...requestUrl " + requestUrl, token);
						expired = true;
					} else {
						if (JwtHelper.getUserId(claims) == null && (!requestUrl.contains("/front/template/getList")
								&& !requestUrl.contains("/front/product/getDetail")
								&& !requestUrl.contains("/front/user/getUerInfo")
								&& !requestUrl.contains("/front/addr/regions")
								&& !requestUrl.contains("/front/wx/jsconfig"))) {
							logger.warn("token [{}] type... not user requestUrl " + requestUrl, authorization);
							Map<String, Object> map = new HashMap<>();
							map.put("code", ResponseCodeHelper.COMMON_USER_NOT_EXIST);
							map.put("msg", "用户未注册");
							response.setCharacterEncoding("UTF-8");
							response.setContentType("application/json; charset=utf-8");
							response.getWriter().print(JSON.toJSONString(map));
							response.getWriter().close();
							return;
						}

						logger.warn("token [{}] type...requestUrl " + requestUrl, authorization);
						request.setAttribute("claims", claims);
						// logger.info("client[{}] pass",
						// JSON.toJSONString(claims));
					}

				} catch (Exception e) {
					logger.error(e.getMessage(), e);
					expired = true;
				}
			}

		}

//		// TODO 调试
//
//		Map<String, Object> claimsMap = new HashMap<>();
//		claims = new DefaultClaims(claimsMap);
//		claims.put("userId", 11609);
//		claims.put("openId", "o15zyt6xxjtakdzpEZnU8c4B-U4c");
//		request.setAttribute("claims", claims);

		// TODO 调试
		if (expired) {
			// logger.error("expired true return ");
			Map<String, Object> map = new HashMap<>();
			map.put("expired", true);
			response.setCharacterEncoding("UTF-8");
			response.setContentType("application/json; charset=utf-8");
			response.getWriter().print(JSON.toJSONString(map));
			response.getWriter().close();
		} else {
			// 频率校验
			String ip = getIpAddress(servletRequest);
			if (!verifyFrequency(ip)) {
				logger.info("频繁请求被拦截 " + claims.toString() + " ip = " + ip + " requestUrl" + requestUrl);
				/*
				 * Map<String, Object> map = new HashMap<>(); map.put("remark",
				 * "请求过于频繁, 请稍后再试"); response.setCharacterEncoding("UTF-8");
				 * response.setContentType("application/json; charset=utf-8");
				 * response.getWriter().print(JSON.toJSONString(map));
				 * response.getWriter().close(); return;
				 */
			}

			chain.doFilter(request, response);
		}

	}

	// 对于高频访问者的阻塞时间
	private final int BLOCK_CACHE_TIME = 5;
	// 对于访问计数缓存时间
	private final int COUNT_CACHE_TIME = 120;
	// 100秒内访问访问限制次数
	private final int BLACK_TIME_LIMIT = 10000;

	private final String CACHE_BLACK_IP_PROFIX = "black";

	private final String CACHE_COUNT_IP_TIMES_PROFIX = "count";

	/**
	 * 如果在黑名单中则限制5秒不允许访问 如果100秒内访问次数超过限制访问次数, 则加入黑名单 否则只计数
	 */
	private boolean verifyFrequency(final String ip) {
		String blackKey = CACHE_BLACK_IP_PROFIX + ip;
		if (cacheManagerUtil.isExist(blackKey)) {
			return false;
		}

		// 100 秒内的访问次数
		String strTime = String.valueOf(System.currentTimeMillis()).substring(0, 8);
		String countKey = CACHE_COUNT_IP_TIMES_PROFIX + strTime + ip;

		int countTimes = 0;
		String value = (String) cacheManagerUtil.jGet(countKey);
		if (!StringUtils.isEmpty(value)) {
			countTimes = Integer.valueOf(value) + 1;
			logger.info("countTimes " + countTimes + " ip " + ip);
			if (countTimes > BLACK_TIME_LIMIT) { // 10秒内访问次数大于限定次数
				cacheManagerUtil.jSet(blackKey, "", BLOCK_CACHE_TIME);
				logger.error("black ip " + blackKey + " iTimes " + countTimes);
			}
		}

		cacheManagerUtil.jSet(countKey, String.valueOf(countTimes), COUNT_CACHE_TIME);
		return true;

	}

	public static void main(String[] args) {
		System.out.println(System.currentTimeMillis());
		System.out.println(String.valueOf(System.currentTimeMillis()).substring(0, 9));
	}

	/**
	 * 获取请求主机IP地址,如果通过代理进来，则透过防火墙获取真实IP地址;
	 * 
	 * @param request
	 * @return
	 * @throws IOException
	 */
	public String getIpAddress(HttpServletRequest request) throws IOException {
		// 获取请求主机IP地址,如果通过代理进来，则透过防火墙获取真实IP地址

		String ip = request.getHeader("X-Forwarded-For");

		if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
			if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
				ip = request.getHeader("Proxy-Client-IP");
			}
			if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
				ip = request.getHeader("WL-Proxy-Client-IP");
			}
			if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
				ip = request.getHeader("HTTP_CLIENT_IP");
			}
			if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
				ip = request.getHeader("HTTP_X_FORWARDED_FOR");
			}
			if (ip == null || ip.length() == 0 || "unknown".equalsIgnoreCase(ip)) {
				ip = request.getRemoteAddr();
			}
		} else if (ip.length() > 15) {
			String[] ips = ip.split(",");
			for (int index = 0; index < ips.length; index++) {
				String strIp = (String) ips[index];
				if (!("unknown".equalsIgnoreCase(strIp))) {
					ip = strIp;
					break;
				}
			}
		}
		return ip;
	}

	@Override
	public void destroy() {

	}
}
